home *** CD-ROM | disk | FTP | other *** search
- /* DialControl */
- /* A sample Control Definition */
- /* DialControl is a control designed to simulate those tape control */
- /* wheels on fancy video tape decks. */
- /* You spin the control as you spin the real thing, click in the */
- /* indentation (called Ball here) and rotate it around. */
- /* As with the real thing, this control can rotate around quite a few revolutions. */
-
- /* This sample shows how to do a few interesting things; */
- /* Track a control around a circular (well, sort of) path. */
- /* Communicate with the 'host' application if it wants to be communicated with, */
- /* during the tracking action (something our scroll bar does _not_ do). */
-
- /* How to use it... */
- /* If you'd like to use this control in something other than the sample it's included with, here */
- /* are some hints. */
- /* When you create the control, the initialization routine _forces_ the control rectangle */
- /* to be square, so a real circle is drawn. */
- /* To get called every time the control value changes during TrackControl, put a */
- /* ProcPtr in the control refCon field.... */
- /* void MyTrackProc(void) */
- /* and that will get called every time the control changes value. This will be handy if you */
- /* want to update some other area of the screen when the control moves. */
- /* ••• WARNING! Either put a proc pointer in the refCon, or ZERO IT! */
- /* This def will jump through the refCon if it is non-zero, period. If you put a number in there */
- /* for some reason or other, I bet this thing will crash. */
-
- /* part code returned for the ball is 2, the rest of the control (not ball) is 1 */
-
- /* When you call TrackControl, pass (ProcPtr)-1 as the last TrackControl parameter, that will */
- /* tell the control manager to use the 'built-in' tracking routine, which is what you want. */
-
- /* And that's about it. Enjoy it. */
- /* Oh heck, I forgot.... */
- /* Copyright © 1992, Apple Computer Inc. */
- /* C.K. Haun */
- /* Apple DTS, April 1992 */
-
- /* FIles used */
- /* DialControl.c */
- /* DialControl.make */
-
- #include <Types.h>
- #include <Quickdraw.h>
- #include <Controls.h>
- #include <Events.h>
- #include <Memory.h>
- #include <ToolUtils.h>
-
- /* A small macro, since I'm grabbing the control rectangle all the time */
- #define mTheRect Rect theRect = (*cHandle)->contrlRect
- /* My prototypes for this control */
- void RectOnPoint(Point thePoint, short wide, short hi, Rect *theRect);
- short WhichWay(ControlHandle cHandle, Point new);
- void CalcBallNow(short whatValue, ControlHandle cHandle, Rect *theRect);
- void CalcRegions(ControlHandle cHandle, long theRegion, short which, long *returnlong);
- void DrawIt(ControlHandle cHandle, long thePart, long *returnlong);
- void TestIt(ControlHandle cHandle, long thePoint, long *returnlong);
- void TrackMe(ControlHandle cHandle, long param, long *returnlong);
-
- enum {
- kPBlack = 1, kPDKGray = 4, kPGray = 23, kPLGray = 3
- };
- enum {kWholeControl = 0,kNotBall,kBall, kAllIndicators=129};
- enum {kPosition0=0,kPosition1,kPosition2,kPosition3,kPosition4,kPosition5,kPosition6,kPosition7,kPosition8,kPosition9,kPosition10,kPosition11,kPosition12,kPosition13,kPosition14,kPosition15};
-
- pascal long DIAL(short varCode, ControlHandle cHandle, short message, long param)
- {
- #pragma unused (varCode)
- mTheRect;
- Rect *theBall;
- long retVal = nil;
- WindowPtr temp;
- GetPort(&temp);
- SetPort((*cHandle)->contrlOwner);
-
- switch (message) {
- case drawCntl:
- DrawIt(cHandle, param, &retVal);
- break;
- case testCntl:
- TestIt(cHandle, param, &retVal);
- break;
- case calcCRgns:
- CalcRegions(cHandle, param, message, &retVal);
- break;
- case initCntl:
- /* store a -1 */
- /* in the action proc, so our defProc will get called to track */
- /* the control */
- (*cHandle)->contrlAction = (ProcPtr)-1;
- /* make sure rect passed is square */
- /* since I calculated the indicator for a circular oval. */
- /* if it ain't circular, I make no guarentees */
- if ((theRect.bottom - theRect.top) != (theRect.right - theRect.left)) {
- theRect.right = theRect.left + (theRect.bottom - theRect.top);
- (*cHandle)->contrlRect = theRect;
- }
- /* create a rect that will be the right size */
- /* for the 'thumb' */
- (*cHandle)->contrlData = NewHandleClear(sizeof(Rect));
- theBall = (Rect *)*((*cHandle)->contrlData);
- theBall->top = nil;
- theBall->left = nil;
- theBall->bottom = (theRect.bottom - theRect.top) / 4;
- theBall->right = (theRect.bottom - theRect.top) / 4;
- break;
- case dispCntl:
- /* I didn't create any handles, so I have nothing to */
- /* dispose of */
- break;
- case posCntl:
- /* do normal positioning */
- break;
- case thumbCntl:
- /* I don't have a thumb, so bye bye */
- break;
- case dragCntl:
- /* do normal dragging */
- break;
- case autoTrack:
- TrackMe(cHandle, param, &retVal);
- break;
- case calcCntlRgn:
- case calcThumbRgn:
- /* See the memory manager chapter of Inside Mac VI for an explanaition */
- /* of these two messages. In short, they are 32bit clean calc messages */
- CalcRegions(cHandle, param, message, &retVal);
- break;
-
- }
- SetPort(temp);
- return(retVal);
-
- }
- /* the port is set on every call to our proc actions by me, so we needn't */
- /* fret about it */
-
- /* CalcRegions allows me to tell the COntrol Manager what region my control takes */
- /* up in the window. The COntrol Manager uses it for */
- /* dragging and the like. */
-
- void CalcRegions(ControlHandle cHandle, long theRegion, short which, long *returnlong)
- {
- #pragma unused (returnlong)
- mTheRect;
- OpenRgn();
- /* here I'm checking to see if this is the non-32bit clean version of the calc message. */
- /* if it is, I must clear the hi byte of the region */
- if (which == calcCRgns)
- theRegion = theRegion & 0xFFFFFF;
- if (which == calcCRgns || which == calcCntlRgn) {
- FrameOval(&theRect);
- CloseRgn((RgnHandle)theRegion);
- } else {
- /* I would calc my thumb here, but I just don't have one */
- }
- }
-
- /* DrawIt.....draws the control! */
- /* And I only actually have two parts, the control itself and the indicator ball thing */
-
-
- void DrawIt(ControlHandle cHandle, long thePart, long *returnlong)
- {
- mTheRect;
- Rect theBall;
- Pattern currentPat;
- PenState theState;
- GetPenState(&theState); /* save pen information */
-
- /* get the rect the ball is occupying now */
- CalcBallNow((*cHandle)->contrlValue, cHandle, &theBall);
- switch (thePart) {
- case kWholeControl: /* draw the whole control */
- case kAllIndicators: /* when the thing moves */
- case kNotBall: /* same thing for part 1 */
- /* o' course, I'm getting patterns out of the SYstem pattern */
- /* list here because I _can't_ say */
- /* PenPat(&qd.Gray); */
- /* since the CDEF doesn't have quickdraw globals to reference. */
- /* The application does, and I _could_ use them, but this */
- /* is just as easy. */
- GetIndPattern(currentPat, sysPatListID, kPBlack);
- PenPat(¤tPat);
- PenSize(1, 1);
- FrameOval(&theRect);
- InsetRect(&theRect, 1, 1);
- GetIndPattern(currentPat, sysPatListID, kPDKGray);
- PenPat(¤tPat);
- PenSize(3, 3);
- FrameOval(&theRect);
- InsetRect(&theRect, 3, 3);
- PenSize(1, 1);
- GetIndPattern(currentPat, sysPatListID, kPBlack);
- PenPat(¤tPat);
- FrameOval(&theRect);
- InsetRect(&theRect, 1, 1);
- GetIndPattern(currentPat, sysPatListID, kPGray);
- PenPat(¤tPat);
- PaintOval(&theRect);
- GetIndPattern(currentPat, sysPatListID, kPBlack);
- PenPat(¤tPat);
- PaintOval(&theBall);
- /* may be that we got here with SetCtrlValue, call the refCon proc if any */
- if ((thePart == kAllIndicators) && ((*cHandle)->contrlRfCon) )
- (ProcPtr)((*cHandle)->contrlRfCon)();
- break;
- case kBall: /* draw the ball */
- GetIndPattern(currentPat, sysPatListID, kPBlack);
- PenPat(¤tPat);
- PaintOval(&theBall);
- break;
- }
- SetPenState(&theState);
-
-
- }
-
-
-
- /* TestIt checks to see if the mouse is down inside the control. */
- /* if it is, then I tell the control manager where. */
- /* I have two parts. You are either in the ball (part 2) or */
- /* in the rest of the control (part 1) */
- void TestIt(ControlHandle cHandle, long thePoint, long *returnlong)
- {
- mTheRect;
- Point ourPoint;
- Rect ballRect;
- RgnHandle theR = NewRgn();
- *returnlong = nil;
- CalcBallNow((*cHandle)->contrlValue, cHandle, &ballRect);
- OpenRgn();
- FrameOval(&ballRect);
- CloseRgn(theR);
- ourPoint.v = (thePoint >> 16) & 0xFFFF;
- ourPoint.h = thePoint & 0xFFFF;
- /* hit in ball??? */
- if (PtInRgn(ourPoint, theR)) {
- *returnlong = kBall;
- } else {
- /* hit in control at all?? */
- OpenRgn();
- FrameOval(&ballRect);
- CloseRgn(theR);
- if (PtInRgn(ourPoint, theR))
- *returnlong = kNotBall;
- }
- DisposeRgn(theR);
- }
-
- /* TrackMe is our tacking for the ball thing. */
- /* I want to auto-track (I put -1 in the action proc), so the Control Manager */
- /* is going to call me to to handle all the control movement */
- void TrackMe(ControlHandle cHandle, long param, long *returnlong)
- {
- mTheRect;
- Rect ballRect;
- Pattern currentPat;
- Point nowPoint, oldPoint;
- short oldVal;
- RgnHandle theR = NewRgn();
- RgnHandle theCont = NewRgn();
- long aLong;
- /* only track if the ball has been clicked on */
- if (param == kBall) {
- OpenRgn();
- FrameOval(&theRect);
- CloseRgn(theCont);
- GetMouse(&nowPoint);
- oldPoint = nowPoint;
- while (StillDown()) {
- GetMouse(&nowPoint);
- CalcBallNow((*cHandle)->contrlValue, cHandle, &ballRect);
- OpenRgn();
- FrameOval(&ballRect);
- CloseRgn(theR);
- if (PtInRgn(nowPoint, theCont) && !PtInRgn(nowPoint, theR)) {
- /* moved out of the ball it started in. see where */
- /* save the current value of the control */
- oldVal = (*cHandle)->contrlValue;
- /* WhichWay returns an amount moved (in ball positions) */
- (*cHandle)->contrlValue += WhichWay(cHandle, nowPoint);
- if ((*cHandle)->contrlValue < 0)
- (*cHandle)->contrlValue = 0;
- if ((*cHandle)->contrlValue > (*cHandle)->contrlMax)
- (*cHandle)->contrlValue = (*cHandle)->contrlMax;
- /* did it really change? */
- if (oldVal != (*cHandle)->contrlValue) {
- /* Yes, redraw */
- GetIndPattern(currentPat, sysPatListID, kPGray);
- PenPat(¤tPat);
- PaintRgn(theR);
- CalcBallNow((*cHandle)->contrlValue, cHandle, &ballRect);
- GetIndPattern(currentPat, sysPatListID, kPBlack);
- PenPat(¤tPat);
- PaintOval(&ballRect);
- /* call the application action proc, if there is one */
- if ((*cHandle)->contrlRfCon) {
- (ProcPtr)((*cHandle)->contrlRfCon)();
-
- }
- oldPoint = nowPoint;
-
-
- }
- }
- }
- }
- DisposeRgn(theCont);
- DisposeRgn(theR);
- }
-
- void TrackingActionProc(void)
- {
- Debugger();
- }
-
- /* CalcBallNow sets up the oval rect for one of the 16 positions the */
- /* ball can have in our control. */
- /* Now I'm sure I could have done this better using, like, Math or something, */
- /* but this works and is a lot faster than doing trig. */
-
- void CalcBallNow(short whatValue, ControlHandle cHandle, Rect *theTRect)
- {
- mTheRect;
- /* set up my calculation bases */
- Rect baseBall = *((Rect *)*((*cHandle)->contrlData));
- short center = theRect.left + ((theRect.right -theRect.left) / 2);
- short cWide = (theRect.right -theRect.left);
- short tWide = baseBall.right -baseBall.left;
- short radii = ((cWide / 2) -3) -(tWide / 2);
- Point rectCenter;
- Point targetP;
- /* convert the control value to a position in a 16 position rotary indicator */
- short realPos = whatValue -((whatValue / 16) * 16);
- rectCenter.v = theRect.top + (cWide / 2);
- rectCenter.h = theRect.left + (cWide / 2);
- *theTRect = baseBall;
- /* NOTE: Tweak these values at your Peril! */
- switch (realPos) {
- case kPosition0: /* up */
- targetP.v = rectCenter.v - radii + 3;
- targetP.h = rectCenter.h;
- break;
-
- case kPosition1:
- targetP.v = rectCenter.v - (radii - (radii / 5));
- targetP.h = rectCenter.h + (radii - (radii / 2));
- break;
- case kPosition2:
- targetP.v = rectCenter.v - (radii - (radii / 3));
- targetP.h = rectCenter.h + (radii - (radii / 3));
-
- break;
- case kPosition3:
- targetP.v = rectCenter.v - (radii - (radii / 2));
- targetP.h = rectCenter.h + (radii - (radii / 5));
-
- break;
- case kPosition4: /* right */
- targetP.v = rectCenter.v;
- targetP.h = rectCenter.h + radii - 3;
-
- break;
- case kPosition5:
- targetP.v = rectCenter.v + (radii - (radii / 2));
- targetP.h = rectCenter.h + (radii - (radii / 5));
-
-
- break;
- case kPosition6:
- targetP.v = rectCenter.v + (radii - (radii / 3));
- targetP.h = rectCenter.h + (radii - (radii / 3));
-
- break;
- case kPosition7:
- targetP.v = rectCenter.v + (radii - (radii / 5));
- targetP.h = rectCenter.h + (radii - (radii / 2));
-
- break;
- case kPosition8: /* down */
- targetP.v = rectCenter.v + radii - 3;
- targetP.h = rectCenter.h;
-
- break;
- case kPosition9:
- targetP.v = rectCenter.v + (radii - (radii / 5));
- targetP.h = rectCenter.h - (radii - (radii / 2));
-
- break;
- case kPosition10:
- targetP.v = rectCenter.v + (radii - (radii / 3));
- targetP.h = rectCenter.h - (radii - (radii / 3));
-
- break;
- case kPosition11:
- targetP.v = rectCenter.v + (radii - (radii / 2));
- targetP.h = rectCenter.h - (radii - (radii / 5));
-
- break;
- case kPosition12: /* left */
- targetP.h = rectCenter.h - radii + 3;
- targetP.v = rectCenter.v;
-
- break;
- case kPosition13:
- targetP.v = rectCenter.v - (radii - (radii / 2));
- targetP.h = rectCenter.h - (radii - (radii / 5));
-
- break;
- case kPosition14:
- targetP.v = rectCenter.v - (radii - (radii / 3));
- targetP.h = rectCenter.h - (radii - (radii / 3));
-
- break;
- case kPosition15:
- targetP.v = rectCenter.v - (radii - (radii / 5));
- targetP.h = rectCenter.h - (radii - (radii / 2));
-
- break;
- }
- /* make a rect */
- RectOnPoint(targetP, tWide, tWide, theTRect);
-
- }
-
- /* RectOnPoint is a little utility to center a rectanlge on a point */
- void RectOnPoint(Point thePoint, short wide, short hi, Rect *theRect)
- {
- short hWide = wide / 2;
- short vWide = hi / 2;
- theRect->top = thePoint.v - vWide;
- theRect->bottom = thePoint.v + vWide;
- theRect->left = thePoint.h - hWide;
- theRect->right = thePoint.h + hWide;
- }
-
- /* WhichWay tells me which way the user is moving the ball, so I can */
- /* update the control value and redraw the ball. */
- /* Since there may be a little latency (uuuuh, I may miss a movement) */
- /* I check ahead and behind 4 positions. This '4' was determined empirically, */
- /* you may want to have a longer scan, or a shorter one. */
- short WhichWay(ControlHandle cHandle, Point new)
- {
- short which = 0;
- Rect theTestRect;
- short testNum = 1;
- /* walk forwards and backwards to see if the user has moved into a new */
- /* ball position */
- do {
- CalcBallNow((*cHandle)->contrlValue - testNum, cHandle, &theTestRect);
- if (PtInRect(new, &theTestRect)) {
- which = testNum * -1;
- } else {
- CalcBallNow((*cHandle)->contrlValue + testNum, cHandle, &theTestRect);
- if (PtInRect(new, &theTestRect))
- which = testNum;
-
- }
- testNum++;
- }while (testNum < 4); /* don't go more than 4 positions away */
-
- return(which);
- }
-